/* 

==========================================================

DX490a - Summer 2010

Instructor: Stelios Manousakis

==========================================================

Class 9.2:

Interfacing 6: Wii in SuperCollider

Contents:

• Connecting

- Preparing: step-by-step (5 steps)

- Receiving data

• A sound example using Ctk and Procmod

==========================================================

*/



// ================= WII IN SUPERCOLLIDER =================



// ====== CONNECTING ======

// The Wiimote connects to your computer via bluetooth, using a proprietary format from Nintendo. There are a few different implementations hacking their way through the protocol, with some being more stable than others.

// Most of these implementations can also get data from the Nunchuk add-on, but none yet from the WiiMotionPlus. However, as these add-ons connect to the Wiimote with a SerialPort, it is very easy to get data from then with an Arduino - all you need to do is read the data through the Arduino's analog ins. There is an adapter (Wiichuck), but you can also stick cables to your Nunchuk's or WiiMotionPlus' connectors and easily retreive all data.



// ------ Preparing: step-by-step (5 steps)--


// Connect Wii with DarwiinRemoteOSC

// get DarwiinRemoteOSC here:

"open http://code.google.com/p/darwiinosc/downloads/list".unixCmd


/* 

• This project is based on darwiinremote. It is stable (though I haven't used it extensively). 

// You can also use the MusicController app, but there is no support for nunchuk and IR

• There is another implementation based on the aka.wiimote max/msp object but that one is very shaky. Inside SC, the WiiMote object uses that implementation, but I never got it to work. Supposedly it is perfect in Linux. 

• There is yet another implementation, (the class is called Wiimote), but I didn't get that to work ever either - maybe it was written on a PPC machine... or maybe it just is a bad implementation

• If you feel like spending a bit of money ($20-40), there is also OSCulator, which can talk to the Wiimote and send OSC data to SC

*/


/*

• step 1: 

turn on your computer's bluetooth


• step 2: 

turn on DarwiinRemoteOSC 


• step 3: 

Press buttons 1 & 2 simultaneously; the LED's will flash until your Wii connects


• step 4: 

Go to the preferences of DarwiinRemoteOSC and set the port nr to 57120 (the sclang port)


• step 5: 

Turn on your responders below, and you're good to go!

*/



/* addres space: this is how the incoming OSC data from DarwiinRemoteOSC is named   */

/*

/wii/connected , i

/wii/mousemode , i

/wii/button/a , i

/wii/button/b , i

/wii/button/up , i

/wii/button/down , i

/wii/button/left , i

/wii/button/right , i

/wii/button/minus , i

/wii/button/plus , i

/wii/button/home , i

/wii/button/one , i

/wii/button/two , i

/wii/acc , fff

/wii/orientation , ff

/wii/irdata , ffffffffffff

/wii/batterylevel , f

/nunchuk/joystick , ff

/nunchuk/button/z , i

/nunchuk/button/c , i

/nunchuk/acc , fff

/nunchuk/orientation , ff

*/



// ------ Receiving data --

(


// accelerometer (format is x, y, z)

~accel = OSCresponderNode(nil, '/wii/acc', {arg ...args; 

args[2].postln;

}).add;


// orientation 

~orient = OSCresponderNode(nil, '/wii/orientation', {arg ...args; 

args[2].postln;

}).add;


// buttons:

~buttonA= OSCresponderNode(nil, '/wii/button/a', {arg ...args; 

args[2].postln;

}).add;


~buttonB= OSCresponderNode(nil, '/wii/button/b', {arg ...args; 

args[2].postln;

}).add;


~button1= OSCresponderNode(nil, '/wii/button/one', {arg ...args; 

args[2].postln;

}).add;


~button2= OSCresponderNode(nil, '/wii/button/two', {arg ...args; 

args[2].postln;

}).add;


~buttonLeft= OSCresponderNode(nil, '//wii/button/left', {arg ...args; 

args[2].postln;

}).add;


~buttonRight= OSCresponderNode(nil, '/wii/button/right', {arg ...args; 

args[2].postln;

}).add;


~buttonUp= OSCresponderNode(nil, '/wii/button/up', {arg ...args; 

args[2].postln;

}).add;


~buttonDown= OSCresponderNode(nil, '/wii/button/down', {arg ...args; 

args[2].postln;

}).add;


~buttonPlus= OSCresponderNode(nil, '/wii/button/plus', {arg ...args; 

args[2].postln;

}).add;


~buttonMinus= OSCresponderNode(nil, '/wii/button/minus', {arg ...args; 

args[2].postln;

}).add;


~buttonHome= OSCresponderNode(nil, '/wii/button/home', {arg ...args; 

args[2].postln;

}).add;


// infrared data (will only work with two IR LEDs)

~ir = OSCresponderNode(nil, '/wii/irdata', {arg ...args; 

args[2].postln;

}).add;


// battery level: doesn't seem to work, but no big deal...

~bat = OSCresponderNode(nil, '/wii/batterylevel', {arg ...args; 

args[2].postln;

}).add;


// nunchuck: joystick

~nunJoy = OSCresponderNode(nil, '/nunchuk/joystick', {arg ...args; 

args[2].postln;

}).add;


// nunchuck: accelerometer

~nunAccel = OSCresponderNode(nil, '/nunchuk/acc', {arg ...args; 

args[2].postln;

}).add;


// nunchuck: orientation

~nunOrient = OSCresponderNode(nil, '/nunchuk/orientation', {arg ...args; 

args[2].postln;

}).add;


// nunchuck: button C

~nunZ = OSCresponderNode(nil, '/nunchuk/button/c', {arg ...args; 

args[2].postln;

}).add;


// nunchuck: button Z

~nunC = OSCresponderNode(nil, '/nunchuk/button/z', {arg ...args; 

args[2].postln;

}).add;

)


// remove all OSCresponders:

[~accel, ~orient, ~buttonA, ~buttonB, ~button1, ~button2, ~buttonLeft, ~buttonRight, ~buttonUp, ~buttonDown, ~buttonPlus, ~buttonMinus, ~buttonHome, ~ir, ~bat, ~nunJoy, ~nunAccel, ~nunOrient, ~nunZ, ~nunC].do{|i| i.remove}




// ====== A SOUND EXAMPLE USING CTK AND PROCMOD ======

// This is a similar example with Class7.1 and Class7.2. The implementation is a bit crude, and only a few parameters of the wii are mapped, but you'll get the idea:


(

// === • Interfacing connections: ===

// first, connect your Wiimote, according to the directions above


// === • Sound synthesis ===

// load a simple FM synth, no mapping inside the synth

~combfm = CtkSynthDef( \combFM, { |outbus = 0, freq = 440, harm = 1, modix = 1, delay = 0.1, pan = 0, amp = 0.5, envbus, freqMul = 1|

var car, mod, out, dev, modfreq;

freq = freq * freqMul;

modfreq = Lag.kr(freq * harm);

dev = modix * modfreq;

mod = SinOsc.ar(modfreq, 0, dev);

car = SinOsc.ar(freq + (mod * modix));

out = Out.ar(outbus, (Pan2.ar(CombC.ar(car, 0.5, delay, 0.5), pan, amp) * In.kr(envbus)));
});

/// === • mapping stuff ===


// create as many control buses as the parameters of the controlling interface

~ctrl = 6.collect({CtkControl.play});


// create some envelopes to handle the parameter mapping as you wish

~panEnv = Env([-1, 1], [1], \lin);

~ampEnv = Env([0.001, 0.7], [1], 3);

~modEnv = Env([1, 50], [1], 4);

~freqEnv = Env([40, 3000], [1], 4);

//~cutEnv = Env([50, 8000], [1], 2);

~delEnv = Env([0.001, 0.5], [1], 2);

~harmEnv = Env([0.1, 10], [1], 2);



/// --- the wii controls --- \\\


// buttons:

/*

B starts the procmod

A stops  the procmod

*/

~buttonB= OSCresponderNode(nil, '/wii/button/b', {arg ...args; 

p.play;

}).add;


~buttonA= OSCresponderNode(nil, '/wii/button/a', {arg ...args; 

p.release;

}).add;


// use the accelerometer to deduce positioning in 3space and overall acceleration (this would definitely benefit from some low-pass filtering, that's up to you to implement using mapping examples seen previously) 

~acc = OSCresponderNode(nil, '/wii/acc', {arg ...args; 

var accels, magnitude, pitch, roll, yaw, testyaw;

// scale down incoming data to 0to1 range

accels = [args[2][1], args[2][2], args[2][3]] - 122; // 0 is the middle point

accels = accels / 200; // try to turn it into a 0to1 range


// deduce magnitude of movement:

magnitude = ((accels[0] ** 2) + (accels[1] ** 2) + (accels[2] ** 2)) ** 0.5;

// deduce pitch: in radians

pitch = ((accels[1]/accels[2]).atan) * -1; 

// deduce roll: in radians

roll = (accels[0]/accels[2]).atan;

// deduce yaw: in radians (attn: it gives NaN every now and then...)

testyaw = (accels[0]/accels[1]).atan;

if (testyaw.isValidUGenInput == true, {yaw = testyaw}, {yaw = 0}); // protect from NaN

//[magnitude, pitch, roll, yaw].postln;

~ctrl[1].set([~harmEnv[(pitch + (pi/2)) / pi]]);

~ctrl[0].set([~panEnv[(roll + (pi/2)) / pi]]);

~ctrl[3].set([~delEnv[(yaw + (pi/2)) / pi]]);

~ctrl[5].set([~modEnv[magnitude]]);

}).add;




// === • the procmod ===

p = ProcMod.new(Env([0, 1, 0], [0.1, 3], \sin, 1), id: \testHID, server: s)

.function_({arg group, envbus, server;

var note;

note = ~combfm.new(target:group)

.freqMul_(1)

//.pan_(~ctrl[0]).modix_(~ctrl[1]).amp_(0.5).delay_(~ctrl[3]).freq_(220).harm_(~ctrl[5])

.pan_(~ctrl[0]).modix_(~ctrl[1]).amp_(0.1).delay_(~ctrl[3]).freq_(220).harm_(~ctrl[5])

.envbus_(envbus).play;

});



)


// don't forget to free the OSCresponders!

~acc.remove

~buttonB.remove

~buttonA.remove